﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Sgr.DataCategories.Services
{
    /// <summary>
    /// 默认类别类型服务实现
    /// </summary>
    /// <remarks>
    /// 该服务通过<see cref="ICategoryTypeProvider"/>收集并管理所有类别类型数据。
    /// 使用延迟加载模式，仅在首次访问时初始化数据。
    /// </remarks>
    public class DefaultCategoryTypeService : ICategoryTypeService, IDisposable
    {
        /// <summary>
        /// 用于初始化过程的异步锁
        /// </summary>
        private readonly SemaphoreSlim _initLock = new(1, 1);

        /// <summary>
        /// 缓存的类别类型数据集合
        /// </summary>
        private IReadOnlyList<NameValue>? _datas;

        /// <summary>
        /// 类别类型数据提供程序集合
        /// </summary>
        private readonly IEnumerable<ICategoryTypeProvider> _providers;

        /// <summary>
        /// 初始化类别类型服务
        /// </summary>
        /// <param name="providers">类别类型提供程序集合</param>
        /// <exception cref="ArgumentNullException">当<paramref name="providers"/>为null时抛出</exception>
        public DefaultCategoryTypeService(IEnumerable<ICategoryTypeProvider> providers)
        {
            _providers = providers ?? throw new ArgumentNullException(nameof(providers));
        }

        /// <summary>
        /// 获取所有类别类型数据
        /// </summary>
        /// <returns>包含所有类别类型的数组</returns>
        /// <remarks>
        /// 首次调用时会初始化数据。后续调用将返回缓存的数据副本。
        /// </remarks>
        public async Task<NameValue[]> GetAllAsync()
        {
            await EnsureInitializedAsync();
            return _datas!.ToArray();
        }

        /// <summary>
        /// 根据指定的值查找类别类型
        /// </summary>
        /// <param name="value">要查找的类别类型值</param>
        /// <returns>找到的类别类型对象，如果未找到则返回null</returns>
        /// <exception cref="ArgumentNullException">当<paramref name="value"/>为null或空字符串时抛出</exception>
        /// <remarks>
        /// 返回的是匹配项的新实例，以避免外部修改影响缓存数据
        /// </remarks>
        public async Task<NameValue?> GetAsync(string value)
        {
            if (string.IsNullOrEmpty(value))
                throw new ArgumentNullException(nameof(value));

            await EnsureInitializedAsync();
            var item = _datas!.FirstOrDefault(f => f.Value == value);
            return item != null ? new NameValue(item.Name, item.Value) : null;
        }

        /// <summary>
        /// 确保数据已初始化
        /// </summary>
        /// <returns>初始化完成的任务</returns>
        /// <remarks>
        /// 使用<see cref="SemaphoreSlim"/>确保线程安全的初始化过程
        /// </remarks>
        private async Task EnsureInitializedAsync()
        {
            if (_datas == null)
            {
                await _initLock.WaitAsync();
                try
                {
                    _datas ??= GetList();
                }
                finally
                {
                    _initLock.Release();
                }
            }
        }

        /// <summary>
        /// 从所有提供程序获取并合并类别类型列表
        /// </summary>
        /// <returns>排序后的不可变类别类型列表</returns>
        /// <remarks>
        /// 使用<see cref="ConcurrentDictionary{TKey, TValue}"/>确保线程安全，
        /// 通过Value去重，并按Value排序
        /// </remarks>
        private ReadOnlyCollection<NameValue> GetList()
        {
            var result = new ConcurrentDictionary<string, NameValue>();

            foreach (var provider in _providers)
            {
                var categoryTypes = provider.GetCategoryTypes();
                foreach (var categoryType in categoryTypes)
                {
                    result.TryAdd(categoryType.Value, categoryType);
                }
            }

            return result.Values
                .OrderBy(f => f.Value)
                .ToList()
                .AsReadOnly();
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <remarks>
        /// 释放<see cref="_initLock"/>持有的非托管资源
        /// </remarks>
        public void Dispose()
        {
            _initLock.Dispose();
            GC.SuppressFinalize(this);
        }
    }
}